/**
 * 
 */
package br.eti.robsonfranca.clf4j.impl;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import br.eti.robsonfranca.clf4j.enums.ClearOp;
import br.eti.robsonfranca.clf4j.interfaces.Console;

import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * Classic implementation
 * @author Robson
 *
 */
@Singleton
public class DirectConsoleImpl implements Console {

	/**
	 * Prepare an ANSI command to be used with outputConsole.
	 * BEWARE: it is NOT thread-safe (StringBuider) 
	 * @param codes an array of escape codes
	 * @return a String with the proper command
	 */	
	protected String prepANSI(String format, Object ... data) {
		StringBuilder sb = new StringBuilder();
		String ret;
		sb.append((char) 27); // ESCAPE Character
		sb.append('[');
		sb.append(String.format(format, data));
		ret = sb.toString();
		sb = null;
		return ret;
	}
	
	
	// A reader and a writer for sendANSI, printf and scanf
	private final InputStream input;
	private final PrintStream output;
	
	
	private static final int WIDTH = 80;
	private static final int HEIGHT = 24;
	
	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#clrLine(br.eti.robsonfranca.clnf4j.enums.ClearOp)
	 */
	@Override
	public void clrLine(ClearOp clo) {
		// Uses the outputConsole to do the trick. Fair and square
		// according to Wikipedia (@url http://en.wikipedia.org/wiki/ANSI_escape_code)
		outputConsole(prepANSI("%dK", clo.ordinal()));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#clrscr(br.eti.robsonfranca.clnf4j.enums.ClearOp)
	 */
	@Override
	public void clrscr(ClearOp clo) {
		// Uses the outputConsole to do the trick. Fair and square
		// according to Wikipedia (@url http://en.wikipedia.org/wiki/ANSI_escape_code)
		outputConsole(prepANSI("%dJ", clo.ordinal()));
	}

	/* @return the height, in characters
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#getHeight()
	 */
	@Override
	public int getHeight() {
		return HEIGHT; 	// A magic number...
	}

	/* @return the width, in characters
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#getWidth()
	 */
	@Override
	public int getWidth() {
		return WIDTH;	// Another magic number...
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#gotoxy(int, int)
	 */
	@Override
	public void gotoxy(int x, int y) {
		// Uses the outputConsole to do the trick. Fair and square
		// according to Wikipedia (@url http://en.wikipedia.org/wiki/ANSI_escape_code)
		outputConsole(prepANSI("%d;%dH",y,x));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#moveDown(int)
	 */
	@Override
	public void moveDown(int n) {
		outputConsole(prepANSI("%dB",n));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#moveLeft(int)
	 */
	@Override
	public void moveLeft(int n) {
		outputConsole(prepANSI("%dD",n));

	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#moveNextLine(int)
	 */
	@Override
	public void moveNextLine(int n) {
		outputConsole(prepANSI("%dE",n));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#movePrevLine(int)
	 */
	@Override
	public void movePrevLine(int n) {
		outputConsole(prepANSI("%dF",n));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#moveRight(int)
	 */
	@Override
	public void moveRight(int n) {
		outputConsole(prepANSI("%dB",n));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#moveUp(int)
	 */
	@Override
	public void moveUp(int n) {
		outputConsole(prepANSI("%dA",n));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#printf(java.lang.String, java.lang.Object[])
	 */
	@Override
	public void printf(String format, Object... args) {
		outputConsole(String.format(format, args));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#restCurPos()
	 */
	@Override
	public void restCurPos() {
		outputConsole(prepANSI("u"));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#saveCurPos()
	 */
	@Override
	public void saveCurPos() {
		outputConsole(prepANSI("s"));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#scanf(java.lang.String, java.lang.String[])
	 */
	@Override
	public String[] scanf(String format) {
		BufferedReader br = new BufferedReader(new InputStreamReader(input));
		String linha;
		String[] ret = null;
		try {
			linha = br.readLine();
			Pattern p = Pattern.compile(format);
			Matcher m = p.matcher(linha);
			if (m.find()) {
				int c = m.groupCount();
				ret = new String[c];
				for(int i=1;i<=c;i++) {
					ret[i-1] = m.group(i); 
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			ret = null;
		}
		return ret;
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#scrollDown()
	 */
	@Override
	public void scrollDown(int n) {
		outputConsole(prepANSI("%dT",n));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#scrollUp()
	 */
	@Override
	public void scrollUp(int n) {
		outputConsole(prepANSI("%dS",n));
	}

	/* Send ANSI command to console. A default console is ANSI compatible ;)
	 * @param command the ansi command to be sent
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#sendANSI(java.lang.String)
	 */
	@Override
	public void sendANSI(String command) {
		// Outputs the string directly
		outputConsole(command);
	}

	protected void outputConsole(String data) {
		output.print(data);
	}
	
	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#setBackColor(br.eti.robsonfranca.clnf4j.enums.Color)
	 */
	@Override
	public void setBackColor(Color color) {
		outputConsole(prepANSI("%sm",color.ansiCode(false)));
	}

	/* (non-Javadoc)
	 * @see br.eti.robsonfranca.clnf4j.interfaces.Console#setForeColor(br.eti.robsonfranca.clnf4j.enums.Color)
	 */
	@Override
	public void setForeColor(Color color) {
		outputConsole(prepANSI("%sm",color.ansiCode(true)));
	}

	/**
	 * Constructor used for Dependency Injection
	 * @param reader
	 * @param writer
	 */
	@Inject
	public DirectConsoleImpl(InputStream input, PrintStream output) {
		this.input = input;
		this.output = output;
	}

	@Override
	public void hideCursor() {
		outputConsole(prepANSI("?25l"));
		
	}

	@Override
	public void showCursor() {
		outputConsole(prepANSI("?25h"));
	}

	@Override
	public char getChar() {
		try {
			return (char) input.read();
		} catch (Exception e) {
			e.printStackTrace();
			return (char) 0;
		}
	}
	
	// Default constructor
/*	public DefaultConsoleImpl() {
		this(System.in, System.out);
	}*/
}
